/* TEAM AWESOMES
 * -Kylan
 * -Leonardo
 * -Anthony
 *
 * relsend sends a file to the relrecv program
 */

#include "basics.h"
#include "finMessage.h"

///////////////GLOBAL CONSTANTS////////////////

#define DEBUG 1
#define BUFSIZE 1201
#define error(s) { perror(s); exit(1); }

//////////////DECLARE GLOBAL VARS//////////////

int sock; // send and receive socket
char address[16]; // receiver ip address
int port; // receiver port number
char filename[20]; // stores filename

//////////////DECLARE DEFINITIONS//////////////

void parseCommandLine(int argc, char**argv);
int match(const char *string, char *pattern);
void createSocket();
void sendFile();

int main(int argc, char**argv) {
    parseCommandLine(argc, argv);
    createSocket();
    sendFile();
}

void sendFile() {
    // Announce who we're sending data to
    if(DEBUG) { printf("\nSending %s to %s:%d\n", filename, address, port); }

    // Open file
    FILE * file = fopen(filename, "rb");
    if (file == NULL) {
      perror("Invalid File\n");
      exit(1);
    }

    // Get size of the file
    fseek(file, 0, SEEK_END);
    long filesize = ftell(file);
    rewind(file);
    printf("filesize: %ld\n\n", filesize);
    unsigned char * sendData;
    sendData = malloc(1201);
    memset(sendData, 0, BUFSIZE);
    int msgType = 204;
    int contentType = 170;
    sendData[0] = msgType;
    sendData[1] = contentType;
    sendData[2] = 0x00;
    sendData[3] = 0x64;
    int i = 0, j = 4;
    for(; i < strlen(filename); i++) {
      if(filename[i] == 0x00) {
	break;
      }
      else {
	sendData[j] = filename[i];
	j++;  
      }  
    }
    sendData[j] = 0x00;
    j++;
    j = sizeToBytes(filesize, sendData, j);
    for(i = 0; i < j; i++) {
      printf("%02x", sendData[i]);
    }
    printf("\n\n\n");
    for(i = 0; i < j; i++) {
      printf("%c", sendData[i]);
    }
    printf("\n\n\n");

    struct sockaddr_in server_addr;
    struct hostent *recvr;

    recvr = gethostbyname(address);
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    server_addr.sin_addr = *((struct in_addr *) recvr->h_addr);
    bzero(&(server_addr.sin_zero), 8);

    sendto(sock, sendData, j, 0,
	   (struct sockaddr *) &server_addr, sizeof (struct sockaddr));
    memset(sendData, 0, BUFSIZE);
    long curPos = 0;
		int dataSize = 0;
    while(curPos < filesize) {
  
        int byte, i;
        for(i = 0; i < BUFSIZE; i++){
            if((filesize - curPos) > 0) {
                byte = fgetc(file);
                sendData[i] = byte;
                curPos++;
								dataSize++;
            }
            else { break; }
        }
        
        recvr = gethostbyname(address);
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(port);
        server_addr.sin_addr = *((struct in_addr *) recvr->h_addr);
        bzero(&(server_addr.sin_zero), 8);

        sendto(sock, sendData, dataSize, 0,
                (struct sockaddr *) &server_addr, sizeof (struct sockaddr));
				dataSize = 0;
    }
    
    fclose(file);
    //memset(sendData, 0, BUFSIZE);
    //sendData = createFinMessage(filename);
    //sendto(sock, sendData, strlen(sendData), 0, (struct sockaddr *) &server_addr, sizeof(struct sockaddr));
}

int sizeToBytes(long fileSize, char * buffer, int pos) {
  if(fileSize > 0 && fileSize < 256) {
    buffer[pos] = fileSize;
    pos++;
    return pos;
  }
  if(fileSize > 255 && fileSize < 65380) {
    int secondByte;
    secondByte = formatNumberByte(fileSize, 8);
    buffer[pos] = secondByte;
    buffer[pos + 1] = fileSize;
    pos += 2;
    return pos;
  }
  if(fileSize > 65380) {
    int thirdByte;
    int secondByte;
    thirdByte = formatNumberByte(fileSize, 16);
    secondByte = formatNumberByte(fileSize, 8);
    buffer[pos] = thirdByte;
    buffer[pos + 1] = secondByte;
    buffer[pos + 2] = fileSize;
    pos += 3;
    return pos;
  }
}

int formatNumberByte(int size, int hex) {
  return size>>hex;
}

void parseCommandLine(int argc, char**argv) {

    // Validate number of arguments
    if (argc != 4) {
        printf("Invalid arguments, use format: ./awesomeprotocol <filename> <ip address> <port>\n");
        exit(1);
    }

    // Validate filename
    if(match(argv[1],"^[a-zA-Z0-9]+[\\.]?[a-zA-Z0-9]+$")) {
        strcpy(filename, argv[1]);
        if(DEBUG) { printf("++Command Line Arguments++\nFilename: %s\n", filename); }
    }
    else {
        printf("Invalid filename, filename should only include characters a-z A-Z 0-9 and an extension.\n");
        exit(1);
    }

    // Validate ip address
    if(strcmp(argv[2], "localhost") == 0) {
        strcpy(address, argv[2]);
        if(DEBUG) { printf("IP Address: %s\n", address); }
    }
    else if(match(argv[2],"^[1-9][0-9]{0,2}[\\.][0-9]{1,3}[\\.][0-9]{1,3}[\\.][0-9]{1,3}$")) {
        strcpy(address, argv[2]);
        if(DEBUG) { printf("IP Address: %s\n", address); }
    }
    else {
        printf("Invalid IP adress.\n");
        exit(1);
    }

    // Validate port number
    int tempPort;
    if(tempPort = atoi(argv[3])) {
        //Check port range
        if(tempPort < 1025 || tempPort > 65535) {
            printf("Invalid port, port must be in range 1025-65535.\n");
            exit(1);
        }
        else {
            port = tempPort;
            if(DEBUG) { printf("Port: %d\n", port); }
        }
    }
    else { // check if atoi passed
        printf("Invalid port: port must be a number.\n");
        exit(1);
    }
    
}

int match(const char *string, char *pattern) {

    int status;
    regex_t re;

    if (regcomp(&re, pattern, REG_EXTENDED | REG_NOSUB) != 0) {
        return 0;
    }

    status = regexec(&re, string, (size_t) 0, NULL, 0);
    regfree(&re);

    if (status != 0) {
        return 0;
    }

    return 1;
}

void createSocket() {

    struct sockaddr_in server_addr;

    // Generate the socket
    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
        perror("Socket");
        exit(1);
    }

    // Set up struct to receive data from our port and address
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(2000);
    server_addr.sin_addr.s_addr = INADDR_ANY;
    bzero(&(server_addr.sin_zero), 8);

    // Bind the struct to our socket
    if (bind(sock, (struct sockaddr *) &server_addr,
            sizeof (struct sockaddr)) == -1) {
        perror("Bind");
        exit(1);
    }

}
